package com.badrit.core.clusteringAlgorithms
{
	import com.vizsage.as3mathlib.math.alg.Matrix;
	
	
	public class DH extends BaseClusteringAlgorithm
	{

		
		public var currentAlgorithm:int = 0;
		private var deltaObjectiveFunction:Number = 0;
		
		function DH(patterns:Matrix, k:Number){
			super(patterns, k);	
		}
		
		public override function applyAlgorithm():Number{
			var iterations:int = 0;				
			var prevObj:Number = calculateSSE();												
			while(iterations < MAX_ITERATIONS){	
				
				switch(currentAlgorithm){
				case DHB:
					applyCoreDHB();		
					break;		
				case DHF:
					applyCoreDHF();
					break;				
				case ABF:
					applyAlgorithmABF(iterations);
					break;				
				case AFB:
					applyAlgorithmAFB(iterations);
					break;
				default:
					throw new Error("Algorithm not valid");				
				}
				var currObj:Number = calculateSSE();
								
				if(Math.abs(prevObj - currObj) < Epsolon
					|| currObj > prevObj)
					break;
				else
					prevObj = currObj;
					
				iterations ++;
			}
			
			return calculateObjectiveFunction();
		}						
					
		public function applyAlgorithmABF(iteration:Number):void{
			if(iteration % 2 == 0){
				applyCoreDHB();					
			} else {
				applyCoreDHF();
			}								
		}	
		
		public function applyAlgorithmAFB(iteration:Number):void{						
			if(iteration % 2 == 0){
				applyCoreDHF();	
			} else {											
				applyCoreDHB();
			}						
		}
		
		public function applyCoreDHF():void{
			calculateCenters();
			for( var l:int = 0; l < nPatterns; l++){
				var i:int = getClusterForPattern(l);
				
				var ni:int = getClusterPatterns(i).length;
				var x:Array = patterns.$m[l];
				var zi:Array = centers.$m[i];
				if( ni == 1 )
					continue;
				
				var deltai:Number = Number(ni/(ni-1)) * calcSquaredEucledianDistance(x, zi);					
									
				for( var j:int = 0 ; j < kClasses; j++){
					if( j == i )
						continue;
					
					var zj:Array = centers.$m[j];
					var nj:int = getClusterPatterns(j).length;
					var deltaj:Number = Number(nj/(nj+1)) * calcSquaredEucledianDistance(x, zj);
					
					if( deltaj < deltai ){
						assignPatternToCluster(l, j);												
						break;
					}
				}	
				
				var targetCluster:int = getClusterForPattern(l);
				if( targetCluster != i ){
					//addPatternToCluster(x, targetCluster);
					//removePatternFromCluster(x, i);
					calculateCenterOfClass(i);
					calculateCenterOfClass(targetCluster);
				}
																	
			}
		}
		
		protected override function assignPatternToCluster(patternIndex:int, cluster:int):void{
		 	M[patternIndex] = cluster;						
		}
					
		/**
		 * returns the cluster index of the given pattern index
		 **/
		public override function getClusterForPattern(patternIndex:int):int {
			return M[patternIndex];
		}
		
		public function applyCoreDHB():void{
			calculateCenters();
			for( var l:int = 0; l < nPatterns; l++){
				var i:int = getClusterForPattern(l);
				
				var ni:int = getClusterPatterns(i).length;
				var x:Array = patterns.$m[l];
				var zi:Array = centers.$m[i];
				if( ni == 1 )
					continue;
				
				var deltai:Number = Number(ni/(ni-1)) * calcSquaredEucledianDistance(x, zi);					
									
				var minDeltaj:Number = deltai;
				var minj:int = 0;
				
				for( var j:int = 0 ; j < kClasses; j++){
					if( j == i )
						continue;
					
					var zj:Array = centers.$m[j];
					var nj:int = getClusterPatterns(j).length;
					var deltaj:Number = Number(nj/(nj+1)) * calcSquaredEucledianDistance(x, zj);
					
					if( deltaj < deltai ){	
						minDeltaj = deltaj;
						minj = j;																				
					}
				}	
				if( minDeltaj < deltai){
					assignPatternToCluster(l, minj);
				}
				
				var targetCluster:int = getClusterForPattern(l);
				if( targetCluster != i ){
					calculateCenterOfClass(i);
					calculateCenterOfClass(targetCluster);
				}
																	
			}
		}		
	}
}